package com.alibaba.yinan.web.graph;

import com.alibaba.yinan.graph.GraphService;
import com.alibaba.yinan.graph.NodeOccupancyManager;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.jgrapht.graph.DefaultEdge;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
@RestController
@RequestMapping("/graph")
public class GraphController {

    @Autowired
    private GraphService graphService;

    @Autowired
    private NodeOccupancyManager nodeOccupancyManager;

    /* ------------------- 图结构管理 ------------------- */

    @PostMapping("/node")
    public void addNode(@RequestParam String name) {
        graphService.addNode(name);
    }

    @PostMapping("/edge")
    public void addEdge(@RequestParam String from, @RequestParam String to) {
        graphService.addEdge(from, to);
    }

    @PostMapping("/bidirectional")
    public void addBidirectionalEdge(@RequestParam String from, @RequestParam String to) {
        graphService.addBidirectionalEdge(from, to);
    }

    /* ------------------- 占用管理 ------------------- */

    @PostMapping("/listOccupiedNode")
    public List<String> listOccupiedNode() {
        return new ArrayList<>(nodeOccupancyManager.getAllOccupiedNodes());
    }

    @PostMapping("/clear")
    public void clearOccupiedNode() {
        nodeOccupancyManager.clear();
    }

    @PostMapping("/occupy")
    public ResponseEntity<String> occupyNodes(@RequestParam String machineId,
                                              @RequestParam List<String> nodes) {
        boolean success = graphService.occupyPath(machineId, nodes);
        return success ?
                ResponseEntity.ok("占用节点成功") :
                ResponseEntity.badRequest().body("占用失败，存在冲突");
    }

    @PostMapping("/release")
    public ResponseEntity<String> releaseNodes(@RequestParam String machineId,
                                               @RequestParam List<String> nodes) {
        graphService.releasePath(machineId, nodes);
        return ResponseEntity.ok("占用节点释放成功");
    }

    /* ------------------- 状态/路径 ------------------- */

    @PostMapping("/getCurrentNode")
    public List<String> getCurrentNode(@RequestParam(required = false) String agvName) {
        // TODO: 这里可以改成真正的查询逻辑，现在只是 mock 数据
        return List.of("A2", "A3");
    }

    @PostMapping("/status/live")
    public ResponseEntity<Map<String, Object>> getLiveGraphStatus() {
        String currentNode = ""; // TODO: 可以结合 AGV 实时位置更新

        Set<String> occupiedNodes = nodeOccupancyManager.getAllOccupiedNodes();
        List<String> allNodes = new ArrayList<>(graphService.getAllNodes());
        Set<DefaultEdge> allEdges = graphService.getAllEdges();

        List<Map<String, Object>> nodes = allNodes.stream().map(nodeId -> {
            Map<String, Object> map = new HashMap<>();
            Map<String, Object> data = new HashMap<>();
            data.put("id", nodeId);
            data.put("label", nodeId);
            if (nodeId.equals(currentNode)) {
                data.put("current", true);
            }
            if (occupiedNodes.contains(nodeId)) {
                data.put("occupied", true);
            }
            map.put("data", data);
            return map;
        }).collect(Collectors.toList());

        List<Map<String, Object>> edges = allEdges.stream().map(edge -> {
            Map<String, Object> map = new HashMap<>();
            Map<String, Object> data = new HashMap<>();
            String source = graphService.getGraph().getEdgeSource(edge);
            String target = graphService.getGraph().getEdgeTarget(edge);
            data.put("id", source + "-" + target);
            data.put("source", source);
            data.put("target", target);
            data.put("bidirectional", graphService.isBidirectionalEdge(source, target));
            map.put("data", data);
            return map;
        }).collect(Collectors.toList());

        Map<String, Object> result = new HashMap<>();
        result.put("elements", Map.of("nodes", nodes, "edges", edges));
        result.put("currentNode", currentNode);
        result.put("occupied", occupiedNodes);

        return ResponseEntity.ok(result);
    }

    @GetMapping("/path/visual")
    public ResponseEntity<Map<String, Object>> getPathVisual(
            @RequestParam String start,
            @RequestParam String end,
            @RequestParam(required = false) List<String> occupied,
            @RequestParam(required = false) String currentNode) {

        List<String> path = graphService.findShortestPath(
                start,
                end,
                occupied != null ? new HashSet<>(occupied) : null
        );

        List<Map<String, Object>> nodes = path.stream()
                .map(id -> {
                    Map<String, Object> node = new HashMap<>();
                    Map<String, Object> data = new HashMap<>();
                    data.put("id", id);
                    data.put("label", id);
                    if (currentNode != null && currentNode.equals(id)) {
                        data.put("current", true);
                    }
                    if (nodeOccupancyManager.getAllOccupiedNodes().contains(id)) {
                        data.put("occupied", true);
                    }
                    node.put("data", data);
                    return node;
                })
                .collect(Collectors.toList());

        List<Map<String, Object>> edges = new ArrayList<>();
        for (int i = 0; i < path.size() - 1; i++) {
            Map<String, Object> edge = new HashMap<>();
            Map<String, Object> data = new HashMap<>();
            data.put("source", path.get(i));
            data.put("target", path.get(i + 1));
            boolean isBidirectional = graphService.getGraph().containsEdge(path.get(i + 1), path.get(i));
            data.put("bidirectional", isBidirectional);
            edge.put("data", data);
            edges.add(edge);
        }

        Map<String, Object> result = Map.of(
                "path", path,
                "elements", Map.of("nodes", nodes, "edges", edges)
        );

        return ResponseEntity.ok(result);
    }

    @GetMapping("/path")
    public ResponseEntity<Map<String, Object>> getPath(@RequestParam String start,
                                                       @RequestParam String end,
                                                       @RequestParam List<String> occupied) {
        List<String> path = graphService.findShortestPath(start, end, new HashSet<>(occupied));
        return ResponseEntity.ok(Map.of(
                "path", path,
                "route", String.join(" -> ", path)
        ));
    }

    /* ------------------- 初始化 ------------------- */

    @PostConstruct
    public void initGraph() {
        IntStream.rangeClosed(1, 20).forEach(i -> {
            graphService.addNode("A" + i);
            graphService.addNode("B" + i);
            graphService.addNode("C" + i);
        });

        for (int i = 1; i < 20; i++) {
            graphService.addBidirectionalEdge("A" + i, "A" + (i + 1));
            graphService.addEdge("B" + i, "B" + (i + 1));
            graphService.addEdge("C" + i, "C" + (i + 1));
        }

        graphService.addEdge("A5", "C10");
        graphService.addEdge("A20", "B1");
        graphService.addEdge("B20", "C1");
        graphService.addEdge("C20", "A1");

        nodeOccupancyManager.registerGroup("G1", Arrays.asList("A1", "B1", "C1"));
        nodeOccupancyManager.registerGroup("G2", Arrays.asList("A2", "A3"));
    }
}
